// TIM16, CH1 ( AF1 0001)
// PF6
// AHB4 APB2
#include <linux/myhead_for_driver.h>
#include "motor_driver.h"

#define DEV_NAME "motor_driver"

unsigned int major = 0;
unsigned int minor = 0;
struct cdev *motor_cdev;
struct class *motor_cls;
struct device *motor_device;

GPIOx_t *GPIOF = NULL;
TIM16_17_t *TIM16 = NULL;

volatile unsigned int *RCC_AHB4ENR = NULL;
volatile unsigned int *RCC_APB2ENR = NULL;

// #define SPEED_ROUTINE (0x1F4 - 55)
#define SPEED_ROUTINE 0x1F4
#define GAP 90
unsigned short SPEED = SPEED_ROUTINE;

int TIM16_init(void)
{
    GPIOF = (GPIOx_t *)ioremap(GPIOF_TRUTH_ADDR, sizeof(GPIOx_t));
    if (NULL == GPIOF)
    {
        printk("out of memory\n");
        return -ENOMEM;
    }
    TIM16 = (TIM16_17_t *)ioremap(TIM16_TRUTH_ADDR, sizeof(TIM16_17_t));
    if (NULL == TIM16)
    {
        printk("out of memory\n");
        return -ENOMEM;
    }
    RCC_AHB4ENR = (volatile unsigned int *)ioremap(RCC_AHB4_SET_EN_TRUTH_ADDR, 4);
    if (NULL == RCC_AHB4ENR)
    {
        printk("out of memory\n");
        return -ENOMEM;
    }
    RCC_APB2ENR = (volatile unsigned int *)ioremap(RCC_APB2_SET_EN_TRUTH_ADDR, 4);
    if (NULL == RCC_APB2ENR)
    {
        printk("out of memory\n");
        return -ENOMEM;
    }
    // RCC 时钟源 给 PE9 & TIM16
    *RCC_AHB4ENR |= 0x1 << 5;
    *RCC_APB2ENR |= 0x1 << 3;
    // 1.引脚复用 2.以及什么的复用 3.输出配置
    // 复用模式 MODER [19:18] b10
    GPIOF->MODER &= ~(0x3 << 12);
    GPIOF->MODER |= 0x1 << 13;
    // 推挽输出 TYPER [9] b0
    GPIOF->OTYPER &= ~(0x1 << 6);
    // 低速 SPEEDR [19:18] b00
    GPIOF->OSPEEDR &= ~(0x3 << 12);
    // 无上下拉 PUPDR [19:18] b00
    GPIOF->PUPDR &= ~(0x3 << 12);
    // 设定为AF1_TIM16CH1通道复用模式 AFRH [7:4] b0001
    GPIOF->AFRL &= ~(0xF << 24);
    GPIOF->AFRL |= 0x1 << 24;
    // 设置分频值
    // PSC[15:0] b11010000 ; 0xD0
    TIM16->PSC &= ~(0xFFFF << 0);
    TIM16->PSC |= 0xD0 << 0;
    // 设置为递增计数器
    // 边缘模式 CR1 [6:5] b00
    TIM16->CR1 &= ~(0x3 << 5);
    // 向下计数 CR1 [4] b1
    TIM16->CR1 |= 0x1 << 4;
    // 使能auto reload
    // 没有缓冲区 CR1 [7] b0
    TIM16->CR1 &= ~(0x1 << 7);
    // 设定auto reload  ARR [15:0] 0x3E8  (1000d)
    TIM16->ARR &= ~(0xFFFF << 0);
    TIM16->ARR |= 0x47E;
    // 捕获比较寄存器1
    // 禁用比较寄存器缓冲区 CCMR1 [3] b0
    TIM16->CCMR1 &= ~(0x1 << 3);
    // CCR1  b000111110100 ; 0x1F4(500)
    TIM16->CCR1 &= ~(0xFFFF);
    TIM16->CCR1 |= 0;
    // PWM模式选择和设置输出模式
    // PWM模式选择 Register:CCMR1[6:4] b110 ; CCMR1 [16] b0
    TIM16->CCMR1 &= ~(0x7 << 4);
    TIM16->CCMR1 |= 0x6 << 4;
    TIM16->CCMR1 &= ~(0x1 << 16);
    // 设定为输出模式 Register:CCMR1-> CC1S [1:0] 0b00
    TIM16->CCMR1 &= ~(0x3 << 0);
    //???????????Register:BDTR中的MOE (给1或0 [15])
    TIM16->BDTR |= 0x1 << 15;
    // 配置捕获比较1通道的输出极性(Register:CCER->CC1P),使能(Register:CCER->CC1E)
    // 输出极性为高 CC1P [1] b0
    TIM16->CCER &= ~(0x1 << 1);
    // 使能 CC1E [0] b1
    TIM16->CCER |= 0x1 << 0;
    // 使能定时器 Register:CR1->CEN
    // 使能 CEN [0] b1
    TIM16->CR1 |= 0x1 << 0;

    iounmap(GPIOF);
    iounmap(TIM16);
    iounmap(RCC_AHB4ENR);
    iounmap(RCC_APB2ENR);

    return 0;
}

void change_speed(void)
{
    TIM16->CCR1 &= ~(0xFFFF);
    TIM16->CCR1 |= SPEED;
}

ssize_t motor_read(struct file *file, char __user *user, size_t size, loff_t *loff)
{
    return 0;
}

ssize_t motor_write(struct file *file, const char __user *user, size_t size, loff_t *loff)
{
    return 0;
}

long motor_ioctl(struct file *file, unsigned int cmd, unsigned long args)
{
    int motor_stat;
    copy_from_user(&motor_stat, (void *)args, GET_SIZE_FROM_CMD(cmd));
    switch (motor_stat)
    {
    case motor_on:
        SPEED = SPEED_ROUTINE;
        change_speed();
        break;
    case motor_off:
        SPEED = 0;
        change_speed();
        break;
    case motor_1:
        SPEED = SPEED_ROUTINE - GAP;
        change_speed();
        break;
    case motor_2:
        SPEED = SPEED_ROUTINE;
        change_speed();
        break;
    case motor_3:
        SPEED = SPEED_ROUTINE + GAP;
        change_speed();
        break;
    default:
        break;
    }

    return 0;
}

int motor_open(struct inode *inode, struct file *file)
{
    return 0;
}

int motor_close(struct inode *inode, struct file *file)
{
    return 0;
}

static const struct file_operations motor_fops = {
    .read = motor_read,
    .write = motor_write,
    .unlocked_ioctl = motor_ioctl,
    .open = motor_open,
    .release = motor_close,
};

static int __init motor_driver_init(void)
{
    int err = 0;
    dev_t motor_dev_num = 0;
    int res = 0;

    motor_cdev = cdev_alloc();

    if (NULL == motor_cdev)
    {
        err = -ENOMEM;
        goto motor_driver_init_ERR1;
    }

    cdev_init(motor_cdev, &motor_fops);

    if ((res = alloc_chrdev_region(&motor_dev_num, 0, 1, DEV_NAME)))
    {
        err = res;
        goto motor_driver_init_ERR2;
    }
    major = MAJOR(motor_dev_num);
    minor = MINOR(motor_dev_num);

    if ((res = cdev_add(motor_cdev, MKDEV(major, minor), 1)))
    {
        err = res;
        goto motor_driver_init_ERR3;
    }

    motor_cls = class_create(THIS_MODULE, "motor_driver_info");
    if (IS_ERR(motor_cls))
    {
        err = PTR_ERR(motor_cls);
        goto motor_driver_init_ERR4;
    }

    motor_device = device_create(motor_cls, NULL, MKDEV(major, minor), NULL, DEV_NAME);
    if (IS_ERR(motor_device))
    {
        err = PTR_ERR(motor_device);
        goto motor_driver_init_ERR5;
    }
    // motor_init
    if (TIM16_init())
    {
        device_destroy(motor_cls, MKDEV(major, minor));
        goto motor_driver_init_ERR5;
    }
    printk("motor_driver_init successful\n");
    return 0;

motor_driver_init_ERR5:
    class_destroy(motor_cls);
motor_driver_init_ERR4:
    cdev_del(motor_cdev);
motor_driver_init_ERR3:
    unregister_chrdev_region(MKDEV(major, minor), 1);
motor_driver_init_ERR2:
    kfree(motor_cdev);
motor_driver_init_ERR1:
    printk("motor_driver_init failed\n");
    return err;
}

static void __exit motor_driver_exit(void)
{
    device_destroy(motor_cls, MKDEV(major, minor));
    class_destroy(motor_cls);
    cdev_del(motor_cdev);
    unregister_chrdev_region(MKDEV(major, minor), 1);
    kfree(motor_cdev);
    return;
}

module_init(motor_driver_init);
module_exit(motor_driver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("rhf xxx@xxx.com");